package com.czk.zookeeper.zookeepercli.lock.practice;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * zookeeper获取分布式锁的练习
 *
 * @Author:ChenZhangKun
 * @Date: 2021/3/18 10:03
 */
@SuppressWarnings("all")
public class MyLockPractice {
    /**
     * ip地址
     */
    private final String IP = "127.0.0.1:2181";
    /**
     * zookeeper实体
     */
    private ZooKeeper zooKeeper;
    /**
     * 线程计数器
     */
    CountDownLatch countDownLatch = new CountDownLatch(1);
    /**
     * 根节点
     */
    private static final String LOCK_ROOT_PATH = "/lock";
    /**
     * 临时有序节点
     */
    private static final String LOCK_NODE_NAME = "Lock_";
    /**
     * 组合的地址
     */
    private String lockPath;

    /**
     * 构造方法连接zookeeper服务器
     *
     * @throws IOException
     * @throws InterruptedException
     */
    public MyLockPractice() throws IOException, InterruptedException {
        zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // 如果是连接上
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    System.out.println("连接成功");
                    // 计数器减一
                    countDownLatch.countDown();
                }
            }
        });
        // 阻塞主线程
        countDownLatch.await();
    }

    /**
     * 创建根节点下的临时有序节点
     *
     * @throws Exception
     */
    public  void createLock() throws Exception {
        // 根节点是否存在
        synchronized (Lock.class){
            Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);
            if (stat == null) {
                // 不存在，创建根节点
                // 非安全的的持久化节点
                zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        }
        // 在创建根节点下的临时有序节点
        lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("节点路径是" + lockPath);
    }

    /**
     * 监视器
     */
    Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent watchedEvent) {
            // 监听节点被删除的时间
            if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
                // 节点被删除
                // 唤醒后续节点
                synchronized (watcher) {
                    watcher.notifyAll();
                }
            }
        }
    };

    /**
     * 尝试获取锁
     *
     * @throws Exception
     */
    public void attempLock() throws Exception {
        // 拿到根节点下的所有子节点
        List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
        // 将子节点进行排序
        Collections.sort(children);
        // 拿到自己的节点的索引
        int i = children.indexOf(lockPath.substring(LOCK_NODE_NAME.length() + 1));
        // 如果是第一个，则获取锁
        if (i == 0) {
            System.out.println("获取锁，进行业务代码");
        } else {
            // 获取上个一个的几点路径
            String prePath = children.get(i - 1);
            // 判断上一个节点是否存在啊
            Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + prePath, watcher);
            // 如果不存在
            if (stat == null) {
                // 尝试获取锁
                attempLock();
            } else {
                // 存在，线程休眠，等待被唤醒
                synchronized (watcher) {
                    watcher.wait();
                }
                attempLock();
            }
        }
    }

    /**
     * 释放锁
     *
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void releaseLock() throws KeeperException, InterruptedException {
        // 删除节点，关闭版本检查
        zooKeeper.delete(lockPath, -1);
        // 关闭当前的zookeeper连接
        zooKeeper.close();
        System.out.println("释放当前锁");
    }

}
